﻿using System;
using System.Threading;

namespace CI.MSRoboticsEnv
{
    internal class SimIdleManager
    {
        private readonly Action _requestFPS;
        public readonly AutoResetEvent FPSUpdateResetEvent = new AutoResetEvent(false);
        private readonly SimEnv _simEnv;
        public int FPSRequestCount;
        private float _lastRecordedFPS;
        public float SimulationEntityFPS;

        private const int MaxFPSRequestCount = 5;
        private const float AcceptableSimulationFPSRate = 4f;
        private const int MaxSimulationFPSWait = 2500;
        private const int SimulationFPSWaitTimeout = 120000;

        public SimIdleManager(Action requestFPS)
        {
            _simEnv = SimEnv.Instance;
            _requestFPS = requestFPS;
        }

        public void WaitForIdle()
        {
            // save the processing state
            var processingState = _simEnv.HostEnv != null ? _simEnv.HostEnv.ProcessingState : string.Empty;
            var waitingTime = SimulationFPSWaitTimeout;

            bool signaled;

            do
            {

                signaled = FPSUpdateResetEvent.WaitOne(MaxSimulationFPSWait);
                waitingTime -= MaxSimulationFPSWait;

                RestartSimulationIfTakingToLong(waitingTime);

                NotifyProcessingState(signaled);

                RequestFPSUpdate();

            } while ((KeepWaitingForMoreFPSUpdates() || !signaled) && !_simEnv.BreakExit);

            _lastRecordedFPS = SimulationEntityFPS;

            // if processing state is not correct then update it, eg we had to pause)
            if (_simEnv.HostEnv != null && !processingState.Equals(_simEnv.HostEnv.ProcessingState))
            {
                SetProcessingState(processingState);
            }
        }

        private void RestartSimulationIfTakingToLong(int waitingTime)
        {
            if (waitingTime < 0)
            {
                _simEnv.TriggerServiceAndScenarioRestart("Unable to get FPS down");
            }
        }

        private void NotifyProcessingState(bool signaled)
        {
            if (!signaled || SimulationEntityFPS < AcceptableSimulationFPSRate)
            {
                SetProcessingState(string.Format("Low FPS : {0}", SimulationEntityFPS));
            }
        }

        private bool KeepWaitingForMoreFPSUpdates()
        {
            return SimulationEntityFPS < AcceptableSimulationFPSRate
                && _lastRecordedFPS != SimulationEntityFPS
                && SimulationEntityFPS != 0;
        }

        private void SetProcessingState(string proccessingState)
        {
            if (_simEnv.HostEnv != null)
                _simEnv.HostEnv.ProcessingState = proccessingState;
        }

        private void RequestFPSUpdate()
        {
            if (FPSRequestCount >= MaxFPSRequestCount)
                return;

            Interlocked.Increment(ref FPSRequestCount);

            _requestFPS();
        }
    }
}